package frame.options.simulation.running;

import java.awt.Color;
import java.awt.Toolkit;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;

import javax.swing.SwingWorker;

import model.components.links.link.VirtualLink;
import model.components.links.link.PhysicalLink;
import model.components.networks.network.VirtualNetwork;
import model.components.networks.network.PhysicalNetwork;
import model.components.networks.network.Network;
import model.components.nodes.node.VirtualNode;
import model.components.nodes.node.PhysicalNode;
import frame.MopraFrame;
import frame.networks.tables.VirtualNetworksTableModel;
import frame.networks.tables.PhysicalNetworksTableModel;
import frame.networks.tables.VirtualNetworksTable;
import frame.networks.tables.PhysicalNetworksTable;
import frame.options.simulation.Simulation;
import frame.options.simulation.results.ResultsFrame;

public class RunningSwingWorker extends SwingWorker< Object, Object >
{	
	private Simulation simulation;
	public Simulation getSimulation(){ return simulation; }
	public void setSimulation( Simulation simulation ){ this.simulation = simulation; }
	
	private MopraFrame mopraFrame;
	public MopraFrame getFrame(){ return mopraFrame; }
	public void setFrame( MopraFrame mopraFrame ){ this.mopraFrame = mopraFrame; }
	
	private RunningFrame runningFrame;
	public RunningFrame getRunningFrame(){ return runningFrame; }
	public void setRunningFrame( RunningFrame runningFrame ){ this.runningFrame = runningFrame; }
	
	private List< VirtualNetwork > resultVirtualNetworks;
	public List< VirtualNetwork > getResultVirtualNetworks(){ return resultVirtualNetworks; }
	public void setResultVirtualNetworks( List< VirtualNetwork > resultVirtualNetworks ){ this.resultVirtualNetworks = resultVirtualNetworks; }
	
	public RunningSwingWorker( Simulation simulation, MopraFrame mopraFrame, RunningFrame runningFrame )
	{
		this.setSimulation( simulation );
		this.setFrame( mopraFrame );
		this.setRunningFrame( runningFrame );
	}
	
	@Override
	protected List< PhysicalNetwork > doInBackground() throws Exception
	{
		List< PhysicalNetwork > resultPhysicalNetworks = new ArrayList< PhysicalNetwork >();
		
		for( PhysicalNetwork physicalNetwork: this.getFrame().getSimulator().getPhysicalNetworks() )
		{
			PhysicalNetwork copyPhysicalNetwork = physicalNetwork.getCopy();
			copyPhysicalNetwork.setName( physicalNetwork.getName() );
			resultPhysicalNetworks.add( copyPhysicalNetwork );
		}
		
		this.setResultVirtualNetworks( new ArrayList< VirtualNetwork >() );
		
		for( VirtualNetwork virtualNetwork: this.getFrame().getSimulator().getVirtualNetworks() )
		{
			VirtualNetwork copyVirtualNetwork = virtualNetwork.getCopy();
			copyVirtualNetwork.setName( virtualNetwork.getName() );
			this.getResultVirtualNetworks().add( copyVirtualNetwork );
		}
		
		int rejectedVirtualNetworksNumber = 0;
		this.setProgress( 0 );
		
		this.getRunningFrame().getProgressBar().setMinimum( 0 );
		this.getRunningFrame().getProgressBar().setValue( 0 );
		this.getRunningFrame().getProgressBar().setMaximum( this.getSimulation().getSimulationEndingDate() + 1 );
		
		for( int i = 0; i < this.getSimulation().getSimulationEndingDate() + 1 && !isCancelled(); i++ )
		{
 			this.setProgress( 100 * i / ( this.getSimulation().getSimulationEndingDate() + 1 ) );
 			this.getRunningFrame().getProgressBar().setValue( i + 1 );
 			
 			List< VirtualNetwork > completedVirtualNetworks = this.getSimulation().getCompletedVirtualNetworks( i );
			
			for( VirtualNetwork completedVirtualNetwork: completedVirtualNetworks )
				if( completedVirtualNetwork.getState().equals( VirtualNetwork.ACCEPTED_STATE ) )
					this.getRunningFrame().addTextColor( "Time " + i + ": OUT " + completedVirtualNetwork.getName() + "\n", Color.BLUE );
			
			List< VirtualNetwork > startingVirtualNetworks = this.getSimulation().getStartingVirtualNetworks( i );
			
			for( VirtualNetwork startingVirtualNetwork: startingVirtualNetworks )
				this.getRunningFrame().addTextColor( "Time " + i + ": IN " + startingVirtualNetwork.getName() + "...\n", Color.BLACK );
			
			if( !this.isCancelled() )
				rejectedVirtualNetworksNumber = this.getSimulation().run( i );
			
			for( VirtualNetwork startingVirtualNetwork: startingVirtualNetworks )
			{
				if( startingVirtualNetwork.getState().equals( Network.REJECTED_STATE ) )
					this.getRunningFrame().addTextColor( "REJECTED\n", Color.RED );
				else
					this.getRunningFrame().addTextColor( "ACCEPTED\n", Color.GREEN );
			}
			
  			for( VirtualNetwork virtualNetwork: this.getSimulation().getVirtualNetworks()  )
  				if( virtualNetwork.isChange() == true )
  					virtualNetwork.setTimeStamp( i );
  			
			for( PhysicalNetwork physicalNetwork: this.getSimulation().getPhysicalNetworks()  )
				if( physicalNetwork.isChange() == true )
				{
					PhysicalNetwork copyPhysicalNetwork = physicalNetwork.getCopy();
					copyPhysicalNetwork.setName( physicalNetwork.getName() + "(" + i + ")" );
					copyPhysicalNetwork.setTimeStamp( i );
					
					resultPhysicalNetworks.add( copyPhysicalNetwork );
					physicalNetwork.setChange( false );
				}
			
			for( VirtualNetwork virtualNetwork: this.getSimulation().getVirtualNetworks()  )
				if( virtualNetwork.isChange() == true )
				{
					VirtualNetwork copyVirtualNetwork = virtualNetwork.getCopy();
					copyVirtualNetwork.setName( virtualNetwork.getName() + "(" + i + ")" );
					copyVirtualNetwork.setTimeStamp( i );
					
					this.getResultVirtualNetworks().add( copyVirtualNetwork );
					
					virtualNetwork.setChange( false );
				}
		}
		
		for( PhysicalNetwork physicalNetwork: this.getSimulation().getPhysicalNetworks() )
		{
			for( PhysicalNode physicalNode: physicalNetwork.getPhysicalNodes() )
				physicalNode.setNumber( physicalNode.getInitialNumber() );
			
			for( PhysicalLink physicalLink: physicalNetwork.getPhysicalLinks() )
				physicalLink.setNumber( physicalLink.getInitialNumber() );
		}
		
		for( VirtualNetwork virtualNetwork: this.getSimulation().getVirtualNetworks() )
		{
			for( VirtualNode virtualNode: virtualNetwork.getVirtualNodes() )
				virtualNode.setNumber( virtualNode.getInitialNumber() );
			
			for( VirtualLink virtualLink: virtualNetwork.getVirtualLinks() )
				virtualLink.setNumber( virtualLink.getInitialNumber() );
		}
		
		this.getFrame().getNetworksPanel().getNetworksTablesTabbedPane().setSelectedIndex( 1 );
		
		if( this.isCancelled() )
			this.getRunningFrame().addTextColor( "Cancelling simulation...\n", Color.RED );			
		
	    System.out.println( "Simulation time: " + this.getSimulation().getSimulationEndingDate() );
		System.out.println( "Number of requests: " + this.getSimulation().getVirtualNetworks().size() );
		System.out.println( "Number of requests rejected: " + rejectedVirtualNetworksNumber );
		
		return resultPhysicalNetworks;
	}
	
	@Override
	@SuppressWarnings("unchecked")
	public void done()
    {
		Toolkit.getDefaultToolkit().beep();
    
		RunningFrame runningFrame = this.getRunningFrame();
		Simulation simulation = this.getSimulation();
		VirtualNetworksTable virtualNetworksTable = this.getFrame().getNetworksPanel().getNetworksTablesTabbedPane().getVirtualNetworksTable();
		VirtualNetworksTableModel virtualNetworksTableModel = (VirtualNetworksTableModel) virtualNetworksTable.getModel();
		
		PhysicalNetworksTable physicalNetworksTable = this.getFrame().getNetworksPanel().getNetworksTablesTabbedPane().getPhysicalNetworksTable();
		PhysicalNetworksTableModel physicalNetworksTableModel = (PhysicalNetworksTableModel) physicalNetworksTable.getModel();
		
    	if( !isCancelled() )
    	{
    		runningFrame.addTextColor( "Simulation finished\n", Color.BLACK );
    		runningFrame.getCancelButton().setEnabled( false );
    		
    		simulation.setState( Simulation.DONE_STATE );
    		
    		if( virtualNetworksTable != null )
    			virtualNetworksTableModel.refresh();
    		
    		List< PhysicalNetwork > resultPhysicalNetworks = null;
    		
			try
			{
				resultPhysicalNetworks = (List< PhysicalNetwork >) this.get();
			}
			catch( InterruptedException interruptedException )
			{
				interruptedException.printStackTrace();
			}
			catch( ExecutionException executionException )
			{
				runningFrame.addTextColor( "Simulation has finished with errors\n", Color.RED );
				runningFrame.getTextPane().setEditable( false );
				simulation.setState( Simulation.CANCELED_STATE );
			
				for( VirtualNetwork virtualNetwork: simulation.getVirtualNetworks() )
					virtualNetwork.setState( Network.READY_STATE );
	    		
				virtualNetworksTableModel.refresh();
				executionException.printStackTrace();
			}
			
			if( resultPhysicalNetworks.size() == 0 )
				return;
			
			int simulatorPhysicalNetworksNumber = this.getFrame().getSimulator().getPhysicalNetworks().size();
			
			List< VirtualNetwork > resultVirtualNetworks = this.getResultVirtualNetworks();
			int simulatorVirtualNetworksNumber = this.getFrame().getSimulator().getVirtualNetworks().size();
			
			runningFrame.setVisible( false );
			runningFrame.setResultsFrame( new ResultsFrame( resultPhysicalNetworks, simulatorPhysicalNetworksNumber, resultVirtualNetworks, simulatorVirtualNetworksNumber, simulation ) );
			runningFrame.getResultsFrame().pack();
			runningFrame.getResultsFrame().setVisible( true );
    	}
    	else
    	{
    		runningFrame.addTextColor( "Simulation canceled\n", Color.RED );
    		simulation.setState( Simulation.CANCELED_STATE );
    		
    		List< VirtualNetwork > simulationVirtualNetworks = simulation.getVirtualNetworks();
    		
    		for( VirtualNetwork virtualNetwork: simulationVirtualNetworks )
				virtualNetwork.setState( Network.AVAILABLE_STATE );
    		
    		virtualNetworksTableModel.refresh();
    		
    		List< PhysicalNetwork > simulationPhysicalNetworks = simulation.getPhysicalNetworks();
    		
    		for( PhysicalNetwork physicalNetwork: simulationPhysicalNetworks )
				physicalNetwork.setState( Network.AVAILABLE_STATE );
    		
    		physicalNetworksTableModel.refresh();
    		runningFrame.setVisible( false );
    	}
    	
    	runningFrame.getTextPane().setEditable( false );
    }
	

}
